
{***********************************************************}
{                                                           }
{      Informix Data Access Components                      }
{                                                           }
{      TIfxConnection component                             }
{                                                           }
{      Copyright (c) 2001-2008 Luxena Software Company      }
{      http://www.luxena.com                                }
{      e-mail: support@luxena.com                           }
{                                                           }
{***********************************************************}

unit IfxConnection;

{$I lx.inc}

interface

uses
  SysUtils, Classes, DB, LXResourcePool, IfxTypes, IfxInt8, IfxSqlHdr,
  IfxSqlSType, IfxSqlca, IfxSqlda, IfxSQLProcessor, IfxMonitor, IfxFieldTypes;


type

{ Enumerations }

  TIfxSafeMode = (smConcurent, smPool, smSingle);

  TIfxIsolationLevel = (ilReadUncommitted, ilReadCommitted, ilSerializable,
    ilUnchanged, ilDirtyRead, ilCommitedRead, ilCursorStability, ilRepeatableRead);

  TIfxTransactionAction = (taCommit, taRollback);

  TIfxTableType = (ttUserTable, ttUserView, ttUserPrivSyn, ttUserPubSyn,
    ttSysTable, ttSysView, ttSysPrivSyn, ttSysPubSyn);
  TIfxTableTypes = set of TIfxTableType;

  TIfxTimeOutStatus = (tsEnd, tsBegin, tsTimeOut);


const

{ Connection Parameter Names }

  szSERVER          = 'INFORMIXSERVER';
  szHOST            = 'WIN32HOST';
  szPROTOCOL        = 'WIN32PROTOCOL';
  szSERVICE         = 'WIN32SERVICE';
  szOPTIONS         = 'WIN32OPTIONS';
  szUSER            = 'WIN32USER';
  szPASSWORD        = 'WIN32PASS';
  szDATABASE        = 'DATABASE';
  szCLIENTLOCALE    = 'CLIENT_LOCALE';
  szDBLOCALE        = 'DB_LOCALE';

{ Property Default Values }

  IFX_DEFAULT_KEEP_CONNECTION = True;
  IFX_DEFAULT_DESIGN_CONNECTION = False;
  IFX_DEFAULT_ISOLATION_LEVEL = ilUnchanged;
  IFX_DEFAULT_SAFE_MODE = smConcurent;
  IFX_DEFAULT_USE_TIMEOUT = False;
  IFX_DEFAULT_TIMEOUT_INTERVAL = 200;
  IFX_DEFAULT_ANSI_AUTOCOMMIT = True;
  IFX_DEFAULT_TRANSACTION_ACTION_ON_CLOSE = taCommit;
  IFX_DEFAULT_COMMIT_RETAIN = True;
  IFX_DEFAULT_STATEMENT_CACHE_SIZE = 20;
  IFX_DEFAULT_SHARE_CURSOR_STATEMENT = False;
  IFX_DEFAULT_TABLE_TYPES = [ttUserTable, ttUserView, ttUserPrivSyn, ttUserPubSyn];

  DATABASE_CHANGING_STMTS = [SQ_DATABASE, SQ_CREADB, SQ_CLSDB];
  RESULTS_RETURNING_STMTS = [SQ_SELECT, SQ_EXECPROC];


type

{ IIfxConnectionProvider }

  TIfxConnection = class;

  IIfxConnectionProvider = interface
    ['{D21E167A-419E-4CEC-A8A3-AAA3D66305DF}']
    function GetConnection: TIfxConnection;
    property Connection: TIfxConnection read GetConnection;
  end;


{ TIfxConnectionParams }

  TIfxConnectionParams = class(TLXPoolResourceFactory)
  private
    FRefCount: Integer;
    FKeepConnection: Boolean;
    FEnvironment: TStrings;
    FIsolationLevel: TIfxIsolationLevel;
    FSafeMode: TIfxSafeMode;
    FAnsiAutoCommit: Boolean;
    FTransactionActionOnClose: TIfxTransactionAction;
    FCommitRetain: Boolean;
    FPoolOptions: TLXResourcePoolOptions;
    FStatementCacheSize: Integer;
    FShareCursorStatement: Boolean;

    function GetDatabase: string;
    procedure SetDatabase(const Value: string);
    function GetUser: string;
    procedure SetUser(const Value: string);
    function GetPassword: string;
    procedure SetPassword(const Value: string);

  public
    constructor Create;
    destructor Destroy; override;

    procedure AddRef;
    procedure Release; override;
    function Equals(ResourceFactory: TLXPoolResourceFactory): Boolean; override;
    function CreateClone: TLXPoolResourceFactory; override;
    function CreateResource(const AcquireParams: array of const;
      Collector: TLXResourceCollector): TLXResource; override;
    function PoolOptions: TLXResourcePoolOptions; override;

    procedure Assign(Params: TIfxConnectionParams);

    property KeepConnection: Boolean read FKeepConnection write FKeepConnection;
    property Environment: TStrings read FEnvironment;
    property Database: string read GetDatabase write SetDatabase;
    property User: string read GetUser write SetUser;
    property Password: string read GetPassword write SetPassword;
    property IsolationLevel: TIfxIsolationLevel read FIsolationLevel write FIsolationLevel;
    property SafeMode: TIfxSafeMode read FSafeMode write FSafeMode;
    property AnsiAutoCommit: Boolean read FAnsiAutoCommit write FAnsiAutoCommit;
    property TransactionActionOnClose: TIfxTransactionAction
      read FTransactionActionOnClose write FTransactionActionOnClose;
    property CommitRetain: Boolean read FCommitRetain write FCommitRetain;
    property StatementCacheSize: Integer read FStatementCacheSize write FStatementCacheSize;
    property ShareCursorStatement: Boolean read FShareCursorStatement write FShareCursorStatement;
  end;


{ TIfxConnectionResource }

  TIfxStatementResource = class;

  TIfxConnectionResource = class(TLXResourceBase)
  private
    FParams: TIfxConnectionParams;
    FHandle: string;
    FIsTransactionsSupported: Boolean;
    FIsAnsiDatabase: Boolean;
    FIsInformixOnline: Boolean;
    FHasOnlineIndices: Integer;
    FServerVersion: string;
    FIsUniversalServer: Integer;
    FSessionID: Integer;
    FSessionUser: string;
    FInTransaction: Boolean;
    FRestoreDatabase: Boolean;
    FStatements: TList;
    FStatementAllocations: Cardinal;
    FStatementCacheHits: Cardinal;
    FShareCursorStatement: Boolean;
    FHolder: TIfxConnection;

    function GetHasOnlineIndices: Boolean;
    function GetServerVersion: string;
    function GetIsUniversalServer: Boolean;
    procedure ReadSessionID;
    function GetSessionID: Integer;
    procedure ReadSessionUser;
    function GetSessionUser: string;

    function CheckInformixResult: Integer; overload;
    function CheckInformixResult(Status: Integer): Integer; overload;

    procedure InternalAcquire(const Params: array of const);
    procedure HandleDatabaseChange;
    procedure RegisterDatabaseChange;
    procedure ResetFeatures;
    function HasStatementCache: Boolean;
    procedure CacheStatement(Statement: TIfxStatementResource);
    procedure AnsiAutoCommit;
    procedure HandleTimeOut(Status: TIfxTimeOutStatus);

    procedure NotifyMonitor(EventType: TIfxMonitorEventType;
      const Format: string; const Args: array of const);

  protected
    procedure PrepareCollect; override;

  public
    constructor Create(Collector: TLXResourceCollector;
      Params: TIfxConnectionParams; const AcquireParams: array of const);
    destructor Destroy; override;

    procedure Acquire(const Params: array of const); override;

    procedure MakeCurrent;
    procedure MakeDormant;

    procedure OpenDatabase(const Database: string; Exclusive: Boolean);
    procedure CloseDatabase;
    procedure SetIsolation(Level: TIfxIsolationLevel);
    procedure BeginTransaction;
    procedure Commit;
    procedure Rollback;
    function AllocateStatement(const SQL: string; Cacheable: Boolean): TIfxStatementResource;
    function Execute(const SQL: string; Params: TObject): int4;

    property Handle: string read FHandle;
    property IsTransactionsSupported: Boolean read FIsTransactionsSupported;
    property IsAnsiDatabase: Boolean read FIsAnsiDatabase;
    property IsInformixOnline: Boolean read FIsInformixOnline;
    property HasOnlineIndices: Boolean read GetHasOnlineIndices;
    property ServerVersion: string read GetServerVersion;
    property IsUniversalServer: Boolean read GetIsUniversalServer;
    property SessionID: Integer read GetSessionID;
    property SessionUser: string read GetSessionUser;
    property InTransaction: Boolean read FInTransaction;
    property StatementAllocations: Cardinal read FStatementAllocations;
    property StatementCacheHits: Cardinal read FStatementCacheHits;
    property ShareCursorStatement: Boolean read FShareCursorStatement;
  end;


{ TIfxStatementResource }

  TIfxStatementResource = class
  private
    FOwner: TIfxConnectionResource;
    FSQL: string;
    FCacheable: Boolean;
    FHandle: PIfx_Cursor;
    FEstimatedCost: int4;
    FCreationTime: TDateTime;
    FLastUsageTime: TDateTime;
    FUsageCount: Cardinal;
    FRefCount: Cardinal;

    function GetHandleName: string;
    function GetHasResults: Boolean;
    function GetIsDatabaseChanging: Boolean;

    procedure RegisterUsage;
    function CalcPenalty(CurrentTime: TDateTime): Double;

    procedure NotifyMonitor(EventType: TIfxMonitorEventType;
      const Format: string; const Args: array of const);

    function CheckInformixResult: Integer; overload;
    function CheckInformixResult(Status: Integer): Integer; overload;
    
  public
    constructor Create(Owner: TIfxConnectionResource; const SQL: string;
      Cacheable: Boolean);
    destructor Destroy; override;
    procedure AddRef;
    procedure Release;

    function Describe: PIfx_SQLDA;
    procedure Execute(Params: PIfx_SQLDA; Fields: PIfx_SQLDA);
    procedure Open(Params: PIfx_SQLDA; Flags: Integer; var Cursor: PIfx_Cursor);
    function Fetch(Cursor: PIfx_Cursor; Fields: PIfx_SQLDA; Direction: mint;
      Distance: int4): mint;
    procedure Close(Cursor: PIfx_Cursor);
    procedure HandleDatabaseChange;

    property SQL: string read FSQL;
    property Handle: PIfx_Cursor read FHandle;
    property HandleName: string read GetHandleName;
    property HasResults: Boolean read GetHasResults;
    property IsDatabaseChanging: Boolean read GetIsDatabaseChanging;
  end;


{ TIfxConnection }

  TIfxConnectionLoginEvent = procedure(Connection: TIfxConnection;
    LoginParams: TStrings) of object;

  TIfxTimeOutEvent = procedure(Status: TIfxTimeOutStatus) of object;

  TIfxReleaseErrorEvent = procedure (E: Exception) of object;

  TIfxConnection = class(TCustomConnection, IIfxConnectionProvider)
  private
    FDesignConnection: Boolean;
    FParams: TIfxConnectionParams;
    FUseTimeOut: Boolean;
    FStreamedUseTimeOut: Boolean;
    FTimeOutInterval: Cardinal;
    FResource: TIfxConnectionResource;
    FSQLProcessor: TIfxSQLProcessor;
    FMonitor: TIfxCustomMonitor;
    FClients: TList;
    FStmts: TList;
    FHolders: Integer;
    FTableTypes: TIfxTableTypes;
    FFieldTypes: TIfxFieldTypes;
    FOnLogin: TIfxConnectionLoginEvent;
    FOnTimeOut: TIfxTimeOutEvent;
    FOnCloseError: TIfxReleaseErrorEvent;

    function GetVersion: string;
    procedure SetVersion(const Value: string);
    function GetKeepConnection: Boolean;
    procedure SetKeepConnection(Value: Boolean);
    function GetParams: TStrings;
    procedure SetParams(Value: TStrings);
    procedure OnParamsChanging(Sender: TObject);
    function GetIsolationLevel: TIfxIsolationLevel;
    procedure SetIsolationLevel(Value: TIfxIsolationLevel);
    function GetAnsiAutoCommit: Boolean;
    procedure SetAnsiAutoCommit(Value: Boolean);
    function GetCommitRetain: Boolean;
    procedure SetCommitRetain(Value: Boolean);
    function GetSafeMode: TIfxSafeMode;
    procedure SetSafeMode(Value: TIfxSafeMode);
    function GetTransactionActionOnClose: TIfxTransactionAction;
    procedure SetTransactionActionOnClose(Value: TIfxTransactionAction);
    function GetPoolOptions: TLXResourcePoolOptions;
    procedure SetPoolOptions(Value: TLXResourcePoolOptions);
    procedure SetUseTimeOut(Value: Boolean);
    function GetStatementCacheSize: Integer;
    procedure SetStatementCacheSize(Value: Integer);
    function GetStatementAllocations: Cardinal;
    function GetStatementCacheHits: Cardinal;
    function GetShareCursorStatement: Boolean;
    procedure SetShareCursorStatement(Value: Boolean);
    procedure SetSQLProcessor(Value: TIfxSQLProcessor);
    procedure SetMonitor(Value: TIfxCustomMonitor);
    procedure SetFieldTypes(Value: TIfxFieldTypes);

    function GetSQLCA: PIfx_sqlca;
    function GetHandle: string;
    function GetServerVersion: string;
    function GetIsInformixOnline: Boolean;
    function GetHasOnlineIndices: Boolean;
    function GetIsUniversalServer: Boolean;
    function GetIsTransactionsSupported: Boolean;
    function GetIsAnsiDatabase: Boolean;
    function GetSessionID: Integer;
    function GetSessionUser: string;
    function GetInTransaction: Boolean;
    function GetStmt(Index: Integer): TObject;
    function GetStmtCount: Integer;
    function GetActiveSQLProcessor: TIfxSQLProcessor;
    function GetActiveFieldTypes: TIfxFieldTypes;
    function GetRowsAffected: Integer;

    function Login: TIfxConnectionParams;
    procedure CheckActive;
    procedure CheckInactive;
    procedure CountHolder(Value: Integer);
    procedure DoTimeOut(Status: TIfxTimeOutStatus);
    function AllocateStatement(const SQL: string; Cacheable: Boolean): TIfxStatementResource;
    procedure HandleDatabaseChange;

    procedure NotifyMonitor(EventType: TIfxMonitorEventType;
      const Format: string; const Args: array of const); overload;
    procedure NotifyMonitor(EventType: TIfxMonitorEventType;
      MessageProc: TIfxMonitorMessageProc; const Args: array of const); overload;

    { IIfxConnectionProvider }
    function GetConnection: TIfxConnection;

  protected
    procedure Loaded; override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure DoConnect; override;
    procedure DoDisconnect; override;
    function GetConnected: Boolean; override;
    procedure RegisterClient(Client: TObject; Event: TConnectChangeEvent = nil); override;
    procedure UnRegisterClient(Client: TObject); override;

  public
    class procedure GetInformixEnvironment(Strings: TStrings);
    class procedure SetInformixEnvironment(Strings: TStrings);
    class procedure GetServerNames(Strings: TStrings);
    class procedure GetHostNames(Strings: TStrings);
    class procedure GetProtocolNames(Strings: TStrings);

    class procedure DeletePools(AsyncDelete: Boolean = True);

    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;

    procedure AssignParams(Connection: TIfxConnection);
    function GetPoolStats: TLXResourcePoolStats;
    procedure DeletePool(AsyncDelete: Boolean = True);
    procedure Activate;
    procedure MakeCurrent;
    procedure MakeDormant;
    procedure GetDatabaseNames(Strings: TStrings);
    procedure OpenDatabase(const Database: string; Exclusive: Boolean = False);
    procedure CloseDatabase;
    procedure StartTransaction;
    procedure Commit;
    procedure Rollback;
    procedure ApplyUpdates(const DataSets: array of TDataSet);
    procedure CloseDataSets;
    procedure OpenTimeOutMode;
    procedure CloseTimeOutMode;
    function InterruptSQL: Boolean;
    procedure GetTableNames(Strings: TStrings); overload;
    procedure GetTableNames(Strings: TStrings; TableTypes: TIfxTableTypes); overload;
    procedure GetStoredProcNames(Strings: TStrings);
    function Execute(const SQL: string; Params: TObject = nil): int4;
    function LastInsertedSerial: int4;
    function LastInsertedRowID: int4;
    function LastInsertedSerial8: TIfx_int8;

    property SQLCA: PIfx_SQLCA read GetSQLCA;
    property Handle: string read GetHandle;
    property ServerVersion: string read GetServerVersion;
    property IsInformixOnline: Boolean read GetIsInformixOnline;
    property HasOnlineIndices: Boolean read GetHasOnlineIndices;
    property IsUniversalServer: Boolean read GetIsUniversalServer;
    property IsTransactionsSupported: Boolean read GetIsTransactionsSupported;
    property IsAnsiDatabase: Boolean read GetIsAnsiDatabase;
    property SessionID: Integer read GetSessionID;
    property SessionUser: string read GetSessionUser;
    property InTransaction: Boolean read GetInTransaction;
    property Stmts[Index: Integer]: TObject read GetStmt;
    property StmtCount: Integer read GetStmtCount;
    property StatementAllocations: Cardinal read GetStatementAllocations;
    property StatementCacheHits: Cardinal read GetStatementCacheHits;
    property ActiveSQLProcessor: TIfxSQLProcessor read GetActiveSQLProcessor;
    property ActiveFieldTypes: TIfxFieldTypes read GetActiveFieldTypes;
    property RowsAffected: int4 read GetRowsAffected;

  published
    property KeepConnection: Boolean read GetKeepConnection write SetKeepConnection default IFX_DEFAULT_KEEP_CONNECTION;
    property Connected;
    property LoginPrompt;
    property DesignConnection: Boolean read FDesignConnection write FDesignConnection default IFX_DEFAULT_DESIGN_CONNECTION;
    property Params: TStrings read GetParams write SetParams;
    property IsolationLevel: TIfxIsolationLevel read GetIsolationLevel write SetIsolationLevel default IFX_DEFAULT_ISOLATION_LEVEL;
    property SafeMode: TIfxSafeMode read GetSafeMode write SetSafeMode default IFX_DEFAULT_SAFE_MODE;
    property UseTimeOut: Boolean read FUseTimeOut write SetUseTimeOut default IFX_DEFAULT_USE_TIMEOUT;
    property TimeOutInterval: Cardinal read FTimeOutInterval write FTimeOutInterval default IFX_DEFAULT_TIMEOUT_INTERVAL;
    property AnsiAutoCommit: Boolean read GetAnsiAutoCommit write SetAnsiAutoCommit default IFX_DEFAULT_ANSI_AUTOCOMMIT;
    property CommitRetain: Boolean read GetCommitRetain write SetCommitRetain default IFX_DEFAULT_COMMIT_RETAIN;
    property TransactionActionOnClose: TIfxTransactionAction
      read GetTransactionActionOnClose write SetTransactionActionOnClose default IFX_DEFAULT_TRANSACTION_ACTION_ON_CLOSE;
    property PoolOptions: TLXResourcePoolOptions read GetPoolOptions write SetPoolOptions;
    property StatementCacheSize: Integer read GetStatementCacheSize write SetStatementCacheSize default IFX_DEFAULT_STATEMENT_CACHE_SIZE;
    property ShareCursorStatement: Boolean read GetShareCursorStatement write SetShareCursorStatement default IFX_DEFAULT_SHARE_CURSOR_STATEMENT;
    property SQLProcessor: TIfxSQLProcessor read FSQLProcessor write SetSQLProcessor;
    property Monitor: TIfxCustomMonitor read FMonitor write SetMonitor;
    property TableTypes: TIfxTableTypes read FTableTypes write FTableTypes default IFX_DEFAULT_TABLE_TYPES;
    property FieldTypes: TIfxFieldTypes read FFieldTypes write SetFieldTypes;
    property Version: string read GetVersion write SetVersion stored False;

    property AfterConnect;
    property AfterDisconnect;
    property BeforeConnect;
    property BeforeDisconnect;
    property OnLogin: TIfxConnectionLoginEvent read FOnLogin write FOnLogin;
    property OnTimeOut: TIfxTimeOutEvent read FOnTimeOut write FOnTimeOut;
    property OnCloseError: TIfxReleaseErrorEvent read FOnCloseError write FOnCloseError;
  end;


{ TIfxConnectionClient }

  TIfxConnectionClient = class(TComponent, IIfxConnectionProvider)
  private
    FConnection: TIfxConnection;
    function GetConnection: TIfxConnection;
    procedure SetConnection(Value: TIfxConnection);
  protected
    procedure RegisterConnection; virtual;
    procedure UnregisterConnection; virtual;
    procedure ConnectionChanging; virtual; abstract;
    procedure ConnectionClosing; virtual; abstract;
    procedure HandleDatabaseChange; virtual; abstract;
    function CheckConnection: TIfxConnection;
    procedure CountHolder(Value: Integer);
    { Access to non public connection's members }
    procedure RegisterConnectionClient(Client: TObject; Event: TConnectChangeEvent);
    procedure UnregisterConnectionClient(Client: TObject);
    function AllocateStatement(const SQL: string; Cacheable: Boolean): TIfxStatementResource;
    procedure NotifyMonitor(EventType: TIfxMonitorEventType;
      const Format: string; const Args: array of const); overload;
    procedure NotifyMonitor(EventType: TIfxMonitorEventType;
      MessageProc: TIfxMonitorMessageProc; const Args: array of const); overload;
  public
    destructor Destroy; override;
    property Connection: TIfxConnection read GetConnection write SetConnection;
  end;


function CheckConnection(Connection: TIfxConnection;
  Component: TComponent = nil): TIfxConnection;


implementation

uses
  Windows, Registry, DBLogDlg, LXConsts, IfxSqlTypes, IfxSqlIApi, IfxSrvInfo,
  IfxConsts, Ifx, IfxIntf, IfxSQL, IfxCustomDataSet;

const
  SQLHOSTS_KEY = '\Software\Informix\SQLHOSTS';
  NETRC_KEY = '\Software\Informix\netrc';

  SESSION_ID_SQL =
    'select dbinfo(''sessionid'') from informix.systables where tabid = 1';

  USER_NAME_SQL =
    'select username from sysmaster:informix.syssessions where sid = ?';

  USER_NAME_SIZE = 32;

  TABLE_NAMES_SQL =
    'select owner, tabname from informix.systables ' +
      'where tabid >= 100 and tabtype in (:usertable, :userview, :userprivsyn, :userpubsyn) ' +
    'union all ' +
    'select owner, tabname from informix.systables ' +
      'where tabid < 100 and tabtype in (:systable, :sysview, :sysprivsyn, :syspubsyn) ' +
    'order by 1, 2';

  PROC_NAMES_SQL =
    'select distinct procname from informix.sysprocedures';

  IS_ONLINE_INDICES_SQL =
    'select 1 ' +
    'from informix.systables t, informix.syscolumns c ' +
    'where t.tabname = ''sysindexes'' and c.tabid = t.tabid and c.colname = ''part16''';

var
  globalConnectionPools: TLXResourcePools;


function CheckConnection(Connection: TIfxConnection;
  Component: TComponent = nil): TIfxConnection;
begin
  if Connection = nil then
    IDACError(SConnectionRequired, Component);
  Result := Connection;
end;


{ TIfxConnectionParams }

constructor TIfxConnectionParams.Create;
begin
  inherited;
  FRefCount := 1;
  FKeepConnection := IFX_DEFAULT_KEEP_CONNECTION;
  FEnvironment := TStringList.Create;
  FIsolationLevel := IFX_DEFAULT_ISOLATION_LEVEL;
  FSafeMode := IFX_DEFAULT_SAFE_MODE;
  FAnsiAutoCommit := IFX_DEFAULT_ANSI_AUTOCOMMIT;
  FCommitRetain := IFX_DEFAULT_COMMIT_RETAIN;
  FTransactionActionOnClose := IFX_DEFAULT_TRANSACTION_ACTION_ON_CLOSE;
  FPoolOptions := TLXResourcePoolOptions.Create;
  FShareCursorStatement := IFX_DEFAULT_SHARE_CURSOR_STATEMENT;
  FStatementCacheSize := IFX_DEFAULT_STATEMENT_CACHE_SIZE;
end;

destructor TIfxConnectionParams.Destroy;
begin
  FEnvironment.Free;
  FPoolOptions.Free;
  inherited;
end;

procedure TIfxConnectionParams.AddRef;
begin
  InterlockedIncrement(FRefCount);
end;

procedure TIfxConnectionParams.Release;
begin
  if InterlockedDecrement(FRefCount) = 0 then Destroy;
end;

function TIfxConnectionParams.Equals(ResourceFactory: TLXPoolResourceFactory): Boolean;
var
  IfxParams: TIfxConnectionParams absolute ResourceFactory;
begin
  Result := FEnvironment.Equals(IfxParams.Environment) and
    FPoolOptions.Equals(IfxParams.PoolOptions);
end;

function TIfxConnectionParams.CreateClone: TLXPoolResourceFactory;
begin
  Result := TIfxConnectionParams.Create;
  try
    TIfxConnectionParams(Result).Assign(Self);
  except
    Result.Free;
    raise;
  end;
end;

function TIfxConnectionParams.CreateResource(const AcquireParams: array of const;
  Collector: TLXResourceCollector): TLXResource;
begin
  Result := TIfxConnectionResource.Create(Collector, Self, AcquireParams);
end;

function TIfxConnectionParams.PoolOptions: TLXResourcePoolOptions;
begin
  Result := FPoolOptions;
end;

procedure TIfxConnectionParams.Assign(Params: TIfxConnectionParams);
begin
  FEnvironment.Assign(Params.FEnvironment);
  FKeepConnection := Params.FKeepConnection;
  FIsolationLevel := Params.FIsolationLevel;
  FSafeMode := Params.FSafeMode;
  FAnsiAutoCommit := Params.FAnsiAutoCommit;
  FTransactionActionOnClose := Params.FTransactionActionOnClose;
  FCommitRetain := Params.FCommitRetain;
  FPoolOptions.Assign(Params.FPoolOptions);
  FStatementCacheSize := Params.FStatementCacheSize;
  FShareCursorStatement := Params.FShareCursorStatement;
end;

function TIfxConnectionParams.GetDatabase: string;
begin
  Result := FEnvironment.Values[szDATABASE];
end;

procedure TIfxConnectionParams.SetDatabase(const Value: string);
begin
  FEnvironment.Values[szDATABASE] := Value;
end;

function TIfxConnectionParams.GetUser: string;
begin
  Result := FEnvironment.Values[szUSER];
end;

procedure TIfxConnectionParams.SetUser(const Value: string);
begin
  FEnvironment.Values[szUSER] := Value;
end;

function TIfxConnectionParams.GetPassword: string;
begin
  Result := FEnvironment.Values[szPASSWORD];
end;

procedure TIfxConnectionParams.SetPassword(const Value: string);
begin
  FEnvironment.Values[szPASSWORD] := Value;
end;


{ TIfxConnectionResource }

constructor TIfxConnectionResource.Create(Collector: TLXResourceCollector;
  Params: TIfxConnectionParams; const AcquireParams: array of const);
var
  Handle: string;
  Server: string;
  Database: string;
begin
  inherited Create(Collector);

  FParams := Params;

  InternalAcquire(AcquireParams);

  Handle := LowerCase(Format('ifxcon$%.*x', [8, Integer(Self)]));

  TIfxConnection.SetInformixEnvironment(Params.Environment);

  Server := ifx_getenv(szSERVER);
  NotifyMonitor(meInformation, SNotifyCreateConn, [Server, Handle]);
  sqli_connect_open(ESQLINTVERSION, 0, PChar('@' + Server), PChar(Handle), nil, 1);
  CheckInformixResult;

  FHandle := Handle;
  Params.AddRef;

  Database := Params.Database;
  if Database <> '' then
  begin
    NotifyMonitor(meInformation, SNotifyOpenDatabase, [FHandle, Database]);
    CheckInformixResult(sqli_db_open(PChar(Database), 0));
  end;

  ResetFeatures;

  SetIsolation(Params.IsolationLevel);

  if Params.StatementCacheSize > 0 then
  begin
    FStatements := TList.Create;
    FShareCursorStatement := Params.ShareCursorStatement;
  end;
end;

destructor TIfxConnectionResource.Destroy;
var
  I: Integer;
  SqlCode: Integer;
begin
  if FHandle <> '' then
    try
      NotifyMonitor(meInformation, SNotifyCloseConn, [FHandle]);

      MakeCurrent;

      if HasStatementCache then
      begin
        for I := 0 to FStatements.Count - 1 do
          TIfxStatementResource(FStatements[I]).Free;
        FStatements.Free;
      end;

      sqli_connect_close(3, nil, 0, 0);
      SqlCode := fnsqlcode^;

      if SqlCode = INVALID_TRANSACTION_STATE then
      begin
        if FParams.TransactionActionOnClose = taCommit then
          SqlCode := sqli_trans_commit else
          SqlCode := sqli_trans_rollback;
      { Informix Online 5.0 generates on disconnect "Invalid transaction state"
        error even if no active transactions, so if commit or rollback fails then
        "database close" have to be called to disconnect from 5.0 server }
        if SqlCode = NOT_IN_TRANSACTION then
        begin
          CheckInformixResult(sqli_db_close);
          sqli_connect_close(3, nil, 0, 0);
          SqlCode := fnsqlcode^;
        end else
        begin
          CheckInformixResult(SqlCode);
          FInTransaction := False;
          sqli_connect_close(3, nil, 0, 0);
          SqlCode := fnsqlcode^;
          if SqlCode = INVALID_TRANSACTION_STATE then
          begin
            CheckInformixResult(sqli_db_close);
            sqli_connect_close(3, nil, 0, 0);
            SqlCode := fnsqlcode^;
          end;
        end;
      end;

      CheckInformixResult(SqlCode);

    finally
      FParams.Release;
    end;

  inherited;
end;

procedure TIfxConnectionResource.Acquire(const Params: array of const);
begin
  InternalAcquire(Params);
  NotifyMonitor(meInformation, SNotifyConnFromPool, [FHandle]);
  MakeCurrent;
end;

procedure TIfxConnectionResource.MakeCurrent;
var
  cur_conn_name: string;
begin
  if FParams.SafeMode <> smSingle then
  begin
    cur_conn_name := ifx_getcur_conn_name;
    if cur_conn_name <> FHandle then
    begin
      sqli_connect_set(0, PChar(FHandle), 0);
      CheckInformixResult;
    end;
  end;
end;

procedure TIfxConnectionResource.MakeDormant;
var
  cur_conn_name: string;
begin
  cur_conn_name := ifx_getcur_conn_name;
  if cur_conn_name = FHandle then
  begin
    sqli_connect_set(0, PChar(FHandle), 1);
    CheckInformixResult;
  end;
end;

procedure TIfxConnectionResource.OpenDatabase(const Database: string;
  Exclusive: Boolean);
begin
  CheckInformixResult(sqli_db_open(PChar(Database), Integer(Exclusive)));
  RegisterDatabaseChange;
end;

procedure TIfxConnectionResource.CloseDatabase;
begin
  CheckInformixResult(sqli_db_close);
  RegisterDatabaseChange;
end;

procedure TIfxConnectionResource.SetIsolation(Level: TIfxIsolationLevel);
var
  LevelString: string;
begin
  if FIsTransactionsSupported and (Level <> ilUnchanged) then
  begin
    case Level of
      ilReadUncommitted, ilDirtyRead: LevelString := 'dirty read';
      ilReadCommitted, ilCommitedRead: LevelString := 'committed read';
      ilSerializable, ilRepeatableRead: LevelString := 'repeatable read';
      ilCursorStability: LevelString := 'cursor stability';
    end;
    NotifyMonitor(meInformation, SNotifySetIsolation, [FHandle, LevelString]);
    try
      CheckInformixResult(sqli_exec_immed(PChar('set isolation to ' + LevelString)));
    except
      on E: Exception do
        IDACErrorFmt(SCannotSetIsolationLevel, [E.Message]);
    end;
  end;
end;

procedure TIfxConnectionResource.BeginTransaction;
var
  Status: mint;
begin
  NotifyMonitor(meInformation, SNotifyBeginTrans, [FHandle]);
  Status := sqli_trans_begin2(1);
  if not FIsAnsiDatabase or (Status <> ALREADY_IN_TRANSACTION)then
    CheckInformixResult(Status);
  FInTransaction := True;
end;

procedure TIfxConnectionResource.Commit;
var
  Status: mint;
begin
  NotifyMonitor(meInformation, SNotifyCommitTrans, [FHandle]);
  Status := sqli_trans_commit;
  if Status = NOT_IN_TRANSACTION then
    FInTransaction := False;
  CheckInformixResult(Status);
  FInTransaction := False;
end;

procedure TIfxConnectionResource.Rollback;
var
  Status: mint;
begin
  NotifyMonitor(meInformation, SNotifyRollbackTrans, [FHandle]);
  Status := sqli_trans_rollback;
  if Status = NOT_IN_TRANSACTION then
    FInTransaction := False;
  CheckInformixResult(Status);
  FInTransaction := False;
end;

function TIfxConnectionResource.AllocateStatement(const SQL: string;
  Cacheable: Boolean): TIfxStatementResource;
var
  I: Integer;
begin
  Inc(FStatementAllocations);
  if Cacheable and HasStatementCache then
    for I := 0 to FStatements.Count - 1 do
    begin
      Result := FStatements[I];
      if (SQL = Result.SQL) and (not Result.HasResults or
        FShareCursorStatement or (Result.FRefCount = 1)) then
      begin
        NotifyMonitor(meInformation, SNotifyStmtFromCache, [FHandle,
          Result.HandleName, SLineBreak, SQL, SLineBreak]);
        Result.AddRef;
        Inc(FStatementCacheHits);
        Exit;
      end;
    end;
  Result := TIfxStatementResource.Create(Self, SQL, Cacheable);
end;

function TIfxConnectionResource.Execute(const SQL: string; Params: TObject): int4;
var
  stmt: TIfx_Statement;
  sqlcmdtxt: array [0..1] of PChar;

  procedure InternalExecute(icnt: mint; ibind: PIfx_sqlvar); overload;
  begin
    CheckInformixResult(sqli_stmt(ESQLINTVERSION, @stmt, @sqlcmdtxt,
      icnt, ibind, nil, nil, nil, nil, -1, 0, 0));
  end;

  procedure InternalExecute(SQLDA: PIfx_sqlda); overload;
  begin
    InternalExecute(SQLDA^.sqld, SQLDA^.sqlvar);
  end;

begin
  sqlcmdtxt[0] := PChar(SQL);
  sqlcmdtxt[1] := nil;
  FillChar(stmt, SizeOf(stmt), 0);
  NotifyMonitor(meInformation, SNotifyExecuteDirect, [FHandle, SLineBreak, SQL]);

  if Params is TIfxParams then
    InternalExecute(TIfxParams(Params).SQLDA)
  else if Params is TParams then
    with TIfxParams.Create do
      try
        Assign(TParams(Params));
        InternalExecute(SQLDA);
      finally
        Free;
      end
  else
    InternalExecute(0, nil);
    
  Result := fnsqlca^.sqlerrd[2];
  if stmt._SQSstmttype in DATABASE_CHANGING_STMTS then
    RegisterDatabaseChange else
    AnsiAutoCommit;
end;

{ Workaround for the INOFRMIX-SE 7.25 IsInformixOnline bug }
function TIfxConnectionResource.GetHasOnlineIndices: Boolean;
var
  cursor: TIfx_cursor;
  sqlcmdtxt: array [0..1] of PChar;
  sqlvar: TIfx_sqlvar;
begin
  if FHasOnlineIndices < 0 then
  begin
    FHasOnlineIndices := 0;
    MakeCurrent;
    FillChar(cursor, SizeOf(cursor), 0);
    sqlcmdtxt[0] := IS_ONLINE_INDICES_SQL;
    sqlcmdtxt[1] := nil;
    FillChar(sqlvar, SizeOf(sqlvar), 0);
    sqlvar.sqltype := CINTTYPE;
    sqlvar.sqllen := SizeOf(FHasOnlineIndices);
    sqlvar.sqldata := @FHasOnlineIndices;
    CheckInformixResult(sqli_slct(ESQLINTVERSION, @cursor, @sqlcmdtxt, 0, nil,
      1, @sqlvar, 0, nil, nil, 0));
  end;
  Result := FHasOnlineIndices > 0;
end;

function TIfxConnectionResource.GetServerVersion: string;
var
  SrvInfo: TIfx_SrvInfo;
begin
  if FServerVersion = '' then
  begin
    MakeCurrent;
    CheckInformixResult(ifx_srvinfo(@SrvInfo));
    FServerVersion := SrvInfo.ProdVersion;
  end;
  Result := FServerVersion;
end;

function TIfxConnectionResource.GetIsUniversalServer: Boolean;
begin
  if FIsUniversalServer = -1 then
  begin
    MakeCurrent;
    FIsUniversalServer := ifx_isius;
  end;
  Result := FIsUniversalServer <> 0;
end;

procedure TIfxConnectionResource.ReadSessionID;
var
  cursor: TIfx_cursor;
  sqlcmdtxt: array [0..1] of PChar;
  sqlvar: TIfx_sqlvar;
begin
  FillChar(cursor, SizeOf(cursor), 0);
  sqlcmdtxt[0] := SESSION_ID_SQL;
  sqlcmdtxt[1] := nil;
  FillChar(sqlvar, SizeOf(sqlvar), 0);
  sqlvar.sqltype := CINTTYPE;
  sqlvar.sqllen := SizeOf(FSessionID);
  sqlvar.sqldata := @FSessionID;
  CheckInformixResult(sqli_slct(ESQLINTVERSION, @cursor, @sqlcmdtxt, 0, nil,
    1, @sqlvar, 0, nil, nil, 0));
end;

function TIfxConnectionResource.GetSessionID: Integer;
begin
  if FSessionID = -1 then
  begin
    MakeCurrent;
    ReadSessionID;
  end;
  Result := FSessionID;
end;

procedure TIfxConnectionResource.ReadSessionUser;
var
  cursor: TIfx_cursor;
  sqlcmdtxt: array [0..1] of PChar;
  sqlvar: array [0..1] of TIfx_sqlvar;
  SessionUser: array [0..USER_NAME_SIZE] of Char;
begin
  ReadSessionID;
  FillChar(cursor, SizeOf(cursor), 0);
  sqlcmdtxt[0] := USER_NAME_SQL;
  sqlcmdtxt[1] := nil;
  FillChar(sqlvar, SizeOf(sqlvar), 0);
  sqlvar[0].sqltype := CINTTYPE;
  sqlvar[0].sqllen := SizeOf(FSessionID);
  sqlvar[0].sqldata := @FSessionID;
  sqlvar[1].sqltype := CCHARTYPE;
  sqlvar[1].sqllen := SizeOf(SessionUser);
  sqlvar[1].sqldata := SessionUser;
  CheckInformixResult(sqli_slct(ESQLINTVERSION, @cursor, @sqlcmdtxt, 1, @sqlvar[0],
    1, @sqlvar[1], 0, nil, nil, 0));
  FSessionUser := Trim(SessionUser);
end;

function TIfxConnectionResource.GetSessionUser: string;
begin
  if FSessionUser = '' then
    if not FIsInformixOnline then
      FSessionUser := FParams.User else
    begin
      MakeCurrent;
      ReadSessionUser;
    end;
  Result := FSessionUser;
end;

procedure TIfxConnectionResource.PrepareCollect;

  procedure RestoreDatabase;
  begin
    if FParams.Database <> '' then
      CheckInformixResult(sqli_db_open(PChar(FParams.Database), 0)) else
      CheckInformixResult(sqli_db_close);
    HandleDatabaseChange;
  end;

begin
  NotifyMonitor(meInformation, SNotifyReleaseConn, [FHandle]);

  if FRestoreDatabase then
    try
      RestoreDatabase;
    except
      on E: EIfxError do
        if E.SQL = MUST_TERMINATE_TRANSACTION then
          try
            if FParams.TransactionActionOnClose = taCommit then
              CheckInformixResult(sqli_trans_commit) else
              CheckInformixResult(sqli_trans_rollback);
            RestoreDatabase;
          except
            on E: Exception do;
          end;
    end;

  MakeDormant;

  FHolder := nil;
end;

function TIfxConnectionResource.CheckInformixResult: Integer;
begin
  Result := CheckInformixResult(fnsqlcode^);
end;

function TIfxConnectionResource.CheckInformixResult(Status: Integer): Integer;
var
  Monitor: TIfxCustomMonitor;
begin
  if FHolder <> nil then
    Monitor := FHolder.Monitor else
    Monitor := nil;

  if Monitor = nil then
    Result := Ifx.CheckInformixResult(Status) else
  begin
    try
      Result := Ifx.CheckInformixResult(Status);
    except
      on E: Exception do
      begin
        Monitor.HandleException(E);
        raise;
      end;
    end;
  end;
end;

procedure TIfxConnectionResource.InternalAcquire(const Params: array of const);
begin
  FHolder := TIfxConnection(Params[0].VObject);
end;

procedure TIfxConnectionResource.HandleDatabaseChange;
var
  I: Integer;
begin
  if HasStatementCache then
  begin
    for I := 0 to FStatements.Count - 1 do
      TIfxStatementResource(FStatements[I]).HandleDatabaseChange;
    FStatements.Clear;
  end;
  ResetFeatures;
  SetIsolation(FParams.IsolationLevel);
end;

procedure TIfxConnectionResource.RegisterDatabaseChange;
begin
  NotifyMonitor(meWarning, SNotifyDatabaseChange, [FHandle]);
  FRestoreDatabase := True;
  if FHolder <> nil then
    FHolder.HandleDatabaseChange;
  HandleDatabaseChange;
end;

procedure TIfxConnectionResource.ResetFeatures;
const
  TRANSACTIONS_MESSAGE: array [Boolean] of string = (SNotifyNotLoggedDatabase,
    SNotifyLoggedDatabase);
  ANSI_COMPLIANT_MESSAGE: array [Boolean] of string = (SNotifyNotAnsiDatabase,
    SNotifyAnsiDatabase);
  INFORMIX_SE_MESSAGE: array [Boolean] of string = (SNotifyNotInformixSE,
    SNotifyInformixSE);
var
  sqlca: PIfx_sqlca;
begin
  sqlca := fnsqlca;
  FIsTransactionsSupported := sqlca^.sqlwarn.sqlwarn1 = 'W';
  FIsAnsiDatabase := sqlca^.sqlwarn.sqlwarn2 = 'W';
  FIsInformixOnline := sqlca^.sqlwarn.sqlwarn3 = 'W';
  if FIsInformixOnline then
    FHasOnlineIndices := -1 else
    FHasOnlineIndices := 0;
  FServerVersion := '';
  FSessionID := -1;
  FIsUniversalServer := -1;
  NotifyMonitor(meInformation, INFORMIX_SE_MESSAGE[not FIsInformixOnline], [FHandle]);
  NotifyMonitor(meInformation, TRANSACTIONS_MESSAGE[FIsTransactionsSupported], [FHandle]);
  NotifyMonitor(meInformation, ANSI_COMPLIANT_MESSAGE[FIsAnsiDatabase], [FHandle]);
end;

function TIfxConnectionResource.HasStatementCache: Boolean;
begin
  Result := FStatements <> nil;
end;

procedure TIfxConnectionResource.CacheStatement(Statement: TIfxStatementResource);
var
  I: Integer;
  Penalty, MaxPenalty: Double;
  MaxPenaltyIndex: Integer;
  CurrentTime: TDateTime;
begin
  if FStatements.IndexOf(Statement) >= 0 then Exit;

  FStatements.Add(Statement);
  Statement.AddRef;

  if FStatements.Count <= FParams.StatementCacheSize then Exit;

  CurrentTime := Now;
  MaxPenalty := 0;
  MaxPenaltyIndex := 0;
  for I := 0 to FStatements.Count - 1 do
  begin
    Penalty := TIfxStatementResource(FStatements[I]).CalcPenalty(CurrentTime);
    if Penalty > MaxPenalty then
    begin
      MaxPenalty := Penalty;
      MaxPenaltyIndex := I;
    end;
  end;

  Statement := FStatements[MaxPenaltyIndex];
  FStatements.Delete(MaxPenaltyIndex);
  Statement.Release;
end;

procedure TIfxConnectionResource.AnsiAutoCommit;
begin
  if FIsAnsiDatabase and FParams.AnsiAutoCommit and not FInTransaction then
  begin
    NotifyMonitor(meInformation, SNotifyAutoCommitTrans, [FHandle]);
    CheckInformixResult(sqli_trans_commit);
  end;
end;

procedure TIfxConnectionResource.HandleTimeOut(Status: TIfxTimeOutStatus);
begin
  if FHolder <> nil then
    FHolder.DoTimeOut(Status);
end;

procedure TIfxConnectionResource.NotifyMonitor(EventType: TIfxMonitorEventType;
  const Format: string; const Args: array of const);
begin
  if FHolder <> nil then
    FHolder.NotifyMonitor(EventType, Format, Args);
end;


{ TIfxStatementResource }

constructor TIfxStatementResource.Create(Owner: TIfxConnectionResource;
  const SQL: string; Cacheable: Boolean);
var
  HandleName: string;
begin
  FOwner := Owner;
  HandleName := LowerCase(Format('ifxsql_%.*x', [8, Integer(Self)]));
  NotifyMonitor(meInformation, SNotifyCreateStmt, [Owner.Handle, HandleName,
    SLineBreak, SQL, SLineBreak]);
  FHandle := sqli_prep(ESQLINTVERSION, PChar(HandleName), PChar(SQL), nil, nil,
    -1, 0, 0);
  CheckInformixResult;
  FSQL := SQL;
  FCacheable := Cacheable;
  FEstimatedCost := fnsqlca^.sqlerrd[3];
  FCreationTime := Now;
  FUsageCount := 1;
  FRefCount := 1;
end;

destructor TIfxStatementResource.Destroy;
begin
  if FHandle <> nil then
  begin
    NotifyMonitor(meInformation, SNotifyDeleteStmt, [HandleName]);
    try
      if (FHandle^._SQCcursptr <> nil) and not FOwner.ShareCursorStatement then
        CheckInformixResult(sqli_curs_free(ESQLINTVERSION, FHandle^._SQCcursptr));
    finally
      CheckInformixResult(sqli_curs_free(ESQLINTVERSION, FHandle));
    end;
  end;
end;

procedure TIfxStatementResource.AddRef;
begin
  Inc(FRefCount);
end;

procedure TIfxStatementResource.Release;
begin
  Dec(FRefCount);
  if FHandle <> nil then
    NotifyMonitor(meInformation, SNotifyReleaseStmt, [HandleName, FRefCount]);
  if FRefCount = 0 then
  begin
    if FHandle <> nil then
      FOwner.MakeCurrent;
    Destroy;
  end;
end;

function TIfxStatementResource.Describe: PIfx_SQLDA;
begin
  CheckInformixResult(sqli_describe_stmt(ESQLINTVERSION, FHandle, @Result, nil));
  RegisterUsage;
end;

procedure TIfxStatementResource.Execute(Params: PIfx_SQLDA; Fields: PIfx_SQLDA);
var
  DatabaseChange: Boolean;
begin
  RegisterUsage;
  NotifyMonitor(meInformation, '%s: %s', [HandleName, SNotifyExecute]);
  DatabaseChange := IsDatabaseChanging;
  CheckInformixResult(sqli_exec(ESQLINTVERSION, FHandle, Params, nil, nil,
    Fields, nil, nil, 0));
  if DatabaseChange then
    FOwner.RegisterDatabaseChange else
    FOwner.AnsiAutoCommit;
end;

procedure TIfxStatementResource.Open(Params: PIfx_SQLDA; Flags: Integer;
  var Cursor: PIfx_Cursor);
var
  HandleName: string;
begin
  RegisterUsage;
  if (FHandle^._SQCcursptr <> nil) and not FOwner.ShareCursorStatement then
  begin
    Cursor := FHandle^._SQCcursptr;
    NotifyMonitor(meInformation, SNotifyCursorFromCache, [Self.HandleName,
      Cursor^._SQCname]);
  end else
  begin
    HandleName := LowerCase(Format('ifxcur_%.*x', [8, Integer(@Cursor)]));
    NotifyMonitor(meInformation, SNotifyDeclareCursor, [Self.HandleName,
      HandleName]);
    Cursor := sqli_curs_locate(ESQLINTVERSION, PChar(HandleName), 512);
    CheckInformixResult;
    try
      CheckInformixResult(sqli_curs_decl_dynm(ESQLINTVERSION, Cursor, nil,
        FHandle, Flags, 0));
    except
      CheckInformixResult(sqli_curs_free(ESQLINTVERSION, Cursor));
      raise;
    end;
  end;
  NotifyMonitor(meInformation, '%s: %s', [Cursor^._SQCname, SNotifyOpen]);
  CheckInformixResult(sqli_curs_open(ESQLINTVERSION, Cursor, Params, nil, nil,
    0, 0));
end;

function TIfxStatementResource.Fetch(Cursor: PIfx_Cursor; Fields: PIfx_SQLDA;
  Direction: mint; Distance: int4): mint;
var
  FetchSpec: T_FetchSpec;
begin
  NotifyMonitor(meInformation, '%s: %s', [Cursor^._SQCname, SNotifyFetch]);
  FetchSpec.fval := Distance;
  FetchSpec.fdir := Direction;
  FetchSpec.findchk := 0;
  Result := CheckInformixResult(sqli_curs_fetch(ESQLINTVERSION, Cursor, nil,
    Fields, nil, @FetchSpec));
end;

procedure TIfxStatementResource.Close(Cursor: PIfx_Cursor);
var
  Status: Integer;
begin
  NotifyMonitor(meInformation, '%s: %s', [Cursor^._SQCname, SNotifyClose]);
  try
    Status := sqli_curs_close(ESQLINTVERSION, Cursor);
    { if cursor was opened without hold then transaction termination closes it
      automatically and cursor already was closed so skip this error }
    if Status <> CURSOR_NOT_OPENED then
      CheckInformixResult(Status);
  finally
    if FOwner.ShareCursorStatement then
      CheckInformixResult(sqli_curs_free(ESQLINTVERSION, Cursor));
    FOwner.AnsiAutoCommit;
  end;
end;

procedure TIfxStatementResource.HandleDatabaseChange;
begin
  FHandle := nil;
  Release;
end;

function TIfxStatementResource.GetHandleName: string;
begin
  Result := FHandle^._SQCname;
end;

function TIfxStatementResource.GetHasResults: Boolean;
begin
  Result := (FHandle^._SQCnfields > 0) and
    (FHandle^._SQCstmttype in RESULTS_RETURNING_STMTS);
end;

function TIfxStatementResource.GetIsDatabaseChanging: Boolean;
begin
  Result := FHandle^._SQCstmttype in DATABASE_CHANGING_STMTS;
end;

procedure TIfxStatementResource.RegisterUsage;
begin
  if FCacheable and FOwner.HasStatementCache then
  begin
    Inc(FUsageCount);
    FLastUsageTime := Now;
    FOwner.CacheStatement(Self);
  end;
end;

function TIfxStatementResource.CalcPenalty(CurrentTime: TDateTime): Double;
begin
  Result := (FEstimatedCost*FEstimatedCost + 1)*
    (SecsPerDay*(2*CurrentTime - FCreationTime - FLastUsageTime) + 1)/
    (FUsageCount*FUsageCount);
end;

procedure TIfxStatementResource.NotifyMonitor(EventType: TIfxMonitorEventType;
  const Format: string; const Args: array of const);
begin
  FOwner.NotifyMonitor(EventType, Format, Args);
end;

function TIfxStatementResource.CheckInformixResult: Integer;
begin
  Result := FOwner.CheckInformixResult;
end;

function TIfxStatementResource.CheckInformixResult(Status: Integer): Integer;
begin
  Result := FOwner.CheckInformixResult(Status);
end;


{ TIfxConnection }

class procedure TIfxConnection.GetInformixEnvironment(Strings: TStrings);

  procedure AddVariable(const Name: string);
  begin
    Strings.Add(Name + '=' + ifx_getenv(PChar(Name)));
  end;

begin
  Strings.BeginUpdate;
  try
    Strings.Clear;
    AddVariable('CC8BITLEVEL');
    AddVariable(szCLIENTLOCALE);
    AddVariable('COLLCHAR');
    AddVariable('CONRETRY');
    AddVariable('CONTIME');
    AddVariable(szDATABASE);
    AddVariable(szDBLOCALE);
    AddVariable('DBALSBC');
    AddVariable('DBANSIWARN');
    AddVariable('DBAPICODE');
    AddVariable('DBASCIIBC');
    AddVariable('DBCENTURY');
    AddVariable('DBCODESET');
    AddVariable('DBCONNECT');
    AddVariable('DBCSCONV');
    AddVariable('DBCSOVERRIDE');
    AddVariable('DBCSWIDTH');
    AddVariable('DBDATE');
    AddVariable('DBFLTMSK');
    AddVariable('DBLANG');
    AddVariable('DBMONEY');
    AddVariable('DBMONEYSCALE');
    AddVariable('DBNLS');
    AddVariable('DBPATH');
    AddVariable('DBSS2');
    AddVariable('DBSS3');
    AddVariable('DBTEMP');
    AddVariable('DBTIME');
    AddVariable('DELIMIDENT');
    AddVariable('ESQLMF');
    AddVariable('FET_BUFF_SIZE');
    AddVariable('GL_DATE');
    AddVariable('GL_DATETIME');
    AddVariable('IFX_AUTOFREE');
    AddVariable('IFX_DEFERRED_PREPARE');
    AddVariable('IFX_USE_PREC_16');
    AddVariable('INFORMIXDIR');
    AddVariable(szSERVER);
    AddVariable('INFORMIXSQLHOSTS');
    AddVariable('LANG');
    AddVariable('LC_COLLATE');
    AddVariable('LC_CTYPE');
    AddVariable('LC_MONETARY');
    AddVariable('LC_NUMERIC');
    AddVariable('LC_TIME');
    AddVariable('NODEFDAC');
    AddVariable('NOZEROMDY');
    AddVariable('OPTMSG');
    AddVariable('OPTOFC');
    AddVariable(szHOST);
    AddVariable(szOPTIONS);
    AddVariable(szPROTOCOL);
    AddVariable(szSERVICE);
    AddVariable(szPASSWORD);
    AddVariable(szUSER);
  finally
    Strings.EndUpdate;
  end;
end;

class procedure TIfxConnection.SetInformixEnvironment(Strings: TStrings);
var
  Index: Integer;
  I: Integer;
begin
  Index := Strings.IndexOfName(szDATABASE);
  for I := 0 to Strings.Count - 1 do
    if (Strings[I] <> '') and (I <> Index) then
      ifx_putenv(PChar(Strings[I]));
end;

class procedure TIfxConnection.GetServerNames(Strings: TStrings);
begin
  with TRegistry.Create do
    try
      RootKey := HKEY_LOCAL_MACHINE;
      if OpenKeyReadOnly(SQLHOSTS_KEY) then
        GetKeyNames(Strings);
    finally
      Free;
    end;
end;

class procedure TIfxConnection.GetHostNames(Strings: TStrings);
begin
  with TRegistry.Create do
    try
      RootKey := HKEY_CURRENT_USER;
      if OpenKeyReadOnly(NETRC_KEY) then
        GetKeyNames(Strings);
    finally
      Free;
    end;
end;

class procedure TIfxConnection.GetProtocolNames(Strings: TStrings);
begin
  with TRegistry.Create do
    try
      RootKey := HKEY_CURRENT_USER;
      if OpenKeyReadOnly(NETRC_KEY) then
        Strings.Text := StringReplace(ReadString('PROTOCOLLIST'), ';',
          SLineBreak, [rfReplaceAll]);
    finally
      Free;
    end;
end;

class procedure TIfxConnection.DeletePools(AsyncDelete: Boolean);
begin
  globalConnectionPools.DeletePools(AsyncDelete);
end;

constructor TIfxConnection.Create(AOwner: TComponent);
begin
  inherited;
  FDesignConnection := IFX_DEFAULT_DESIGN_CONNECTION;
  FParams := TIfxConnectionParams.Create;
  FUseTimeOut := IFX_DEFAULT_USE_TIMEOUT;
  TStringList(FParams.Environment).OnChanging := OnParamsChanging;
  FTimeOutInterval := IFX_DEFAULT_TIMEOUT_INTERVAL;
  FClients := TList.Create;
  FStmts := TList.Create;
  FTableTypes := IFX_DEFAULT_TABLE_TYPES;
end;

destructor TIfxConnection.Destroy;
var
  I: Integer;
begin
  Close;

  if FClients <> nil then
  begin
    for I := FClients.Count - 1 downto 0 do
      TIfxConnectionClient(FClients[0]).Connection := nil;
    FClients.Free;
  end;
  FStmts.Free;
  FParams.Free;
  SetSQLProcessor(nil);
  SetMonitor(nil);
  SetFieldTypes(nil);

  inherited;
end;

procedure TIfxConnection.AfterConstruction;
begin
  inherited;
  TIDACEvents.HandleObjectCreate(Self);
end;

procedure TIfxConnection.BeforeDestruction;
begin
  TIDACEvents.HandleObjectDestroy(Self);
  inherited;
end;

procedure TIfxConnection.AssignParams(Connection: TIfxConnection);
begin
  FParams.Assign(Connection.FParams);
end;

function TIfxConnection.GetPoolStats: TLXResourcePoolStats;
begin
  Result := globalConnectionPools.GetPoolStats(FParams);
end;

procedure TIfxConnection.DeletePool(AsyncDelete: Boolean);
begin
  globalConnectionPools.DeletePool(FParams, AsyncDelete);
end;

function TIfxConnection.GetIsInformixOnline: Boolean;
begin
  CheckActive;
  Result := FResource.IsInformixOnline;
end;

function TIfxConnection.GetServerVersion: string;
begin
  CheckActive;
  Result := FResource.ServerVersion;
end;

function TIfxConnection.GetHasOnlineIndices: Boolean;
begin
  CheckActive;
  Result := FResource.HasOnlineIndices;
end;

function TIfxConnection.GetIsUniversalServer: Boolean;
begin
  CheckActive;
  Result := FResource.IsUniversalServer;
end;

function TIfxConnection.GetSessionID: Integer;
begin
  CheckActive;
  Result := FResource.SessionID;
end;

function TIfxConnection.GetSessionUser: string;
begin
  CheckActive;
  Result := FResource.SessionUser;
end;

function TIfxConnection.GetSQLCA: PIfx_sqlca;
begin
  Result := fnsqlca;
end;

function TIfxConnection.GetStmt(Index: Integer): TObject;
begin
  Result := FStmts[Index];
end;

function TIfxConnection.GetStmtCount: Integer;
begin
  Result := FStmts.Count;
end;

procedure TIfxConnection.OnParamsChanging(Sender: TObject);
begin
  CheckInactive;
end;

procedure TIfxConnection.SetParams(Value: TStrings);
begin
  FParams.Environment.Assign(Value);
end;

procedure TIfxConnection.SetKeepConnection(Value: Boolean);
begin
  if FParams.KeepConnection <> Value then
  begin
    FParams.FKeepConnection := Value;
    CountHolder(0);
  end;
end;

procedure InterruptSQLCallBack(Param: Integer); cdecl;
var
  cur_conn_name: string;
  Resource: TIfxConnectionResource;
begin
  cur_conn_name := ifx_getcur_conn_name;
  if StrLComp(PChar(cur_conn_name), 'ifxcon$', 7) <> 0 then
    IDACError(SConnectionHandleWrong);
  Resource := TIfxConnectionResource(StrToInt(PChar(@cur_conn_name[7])));
  Resource.HandleTimeOut(TIfxTimeOutStatus(Param));
end;

procedure TIfxConnection.SetUseTimeOut(Value: Boolean);
begin
  if FUseTimeOut <> Value then
  begin
    if Connected and not (csDesigning in ComponentState) then
    begin
      MakeCurrent;
      if Value then
        sqlbreakcallback(FTimeOutInterval, InterruptSQLCallBack) else
        sqlbreakcallback(-1, nil);
    end;
    FUseTimeOut := Value;
  end;
end;

procedure TIfxConnection.NotifyMonitor(EventType: TIfxMonitorEventType;
  const Format: string; const Args: array of const);
begin
  if FMonitor <> nil then
    FMonitor.HandleEvent(EventType, Format, Args);
end;

procedure TIfxConnection.NotifyMonitor(EventType: TIfxMonitorEventType;
  MessageProc: TIfxMonitorMessageProc; const Args: array of const);
begin
  if FMonitor <> nil then
    FMonitor.HandleEvent(EventType, MessageProc, Args);
end;

function TIfxConnection.GetConnection: TIfxConnection;
begin
  Result := Self;
end;

procedure TIfxConnection.CheckActive;
begin
  if FResource = nil then
    IDACError(SConnectionClosed, Self);
end;

procedure TIfxConnection.CheckInactive;
begin
  if FResource <> nil then
  begin
    if not (csDesigning in ComponentState) then
      IDACError(SConnectionOpened, Self);
    Close;
  end;
end;

procedure TIfxConnection.CountHolder(Value: Integer);
begin
  FHolders := FHolders + Value;
  if (FHolders = 0) and Connected and not (KeepConnection or InTransaction or
    (csLoading in ComponentState)) then Close;
end;

procedure TIfxConnection.DoTimeOut(Status: TIfxTimeOutStatus);
begin
  if Assigned(FOnTimeout) then
    FOnTimeout(Status);
end;

procedure TIfxConnection.Activate;
begin
  if FResource = nil then SetConnected(True) else MakeCurrent;
end;

procedure TIfxConnection.MakeCurrent;
begin
  CheckActive;
  FResource.MakeCurrent;
end;

procedure TIfxConnection.MakeDormant;
begin
  CheckActive;
  FResource.MakeDormant;
end;

procedure TIfxConnection.GetDatabaseNames(Strings: TStrings);
const
  MAX_COUNT = 1024;
  BUFFER_SIZE = 1024 * 129;
var
  Count: Integer;
  Buffer: PChar;
  DBNames: array [0..MAX_COUNT - 1] of PChar;
  I: Integer;
begin
  Activate;
  Strings.BeginUpdate;
  try
    Buffer := AllocMem(BUFFER_SIZE);
    try
      CheckInformixResult(sqgetdbs(@Count, @DBNames, MAX_COUNT, Buffer, BUFFER_SIZE));
    Strings.Clear;
    for I := 0 to Count - 1 do
      Strings.Add(DBNames[I]);
    finally
      FreeMem(Buffer);
    end;
  finally
    Strings.EndUpdate;
  end;
end;

function TIfxConnection.Login: TIfxConnectionParams;
var
  Database: string;
  User: string;
  Password: string;
begin
  Result := TIfxConnectionParams(FParams.CreateClone);
  try
    if Assigned(FOnLogin) then
      FOnLogin(Self, Result.Environment) else
    begin
      Database := Result.Database;
      User := Result.User;
      if not LoginDialogEx(Database, User, Password, False) then
        IDACError(SCancelLogin, Self);
      Result.User := User;
      Result.Password := Password;
    end;
  except
    Result.Free;
    raise;
  end;
end;

procedure TIfxConnection.DoConnect;
var
  Params: TIfxConnectionParams;
  Resource: TLXResource;
begin
  if Connected then Exit;

  if LoginPrompt then
    Params := Login else
    Params := FParams;
  try
    if FParams.PoolOptions.Enabled then
      Resource := globalConnectionPools.AllocateResource(Params, [Self]) else
      Resource := Params.CreateResource([Self], nil);
    FResource := Resource as TIfxConnectionResource;
  finally
    if LoginPrompt then
      Params.Release;
  end;

  if FUseTimeOut and not (csDesigning in ComponentState) then
    sqlbreakcallback(FTimeOutInterval, InterruptSQLCallBack) else
end;

procedure TIfxConnection.DoDisconnect;
begin
  if not Connected then Exit;

  try
    CloseTimeOutMode;
    FResource.Release;
  except
    on E: Exception do
      if Assigned(FOnCloseError) then FOnCloseError(E);
  end;
  FResource := nil;
end;

function TIfxConnection.GetConnected: Boolean;
begin
  Result := FResource <> nil;
end;

procedure TIfxConnection.Loaded;
begin
  if FDesignConnection and not (csDesigning in ComponentState) then
    StreamedConnected := False;
  inherited;
  try
    if FStreamedUseTimeOut then
      SetUseTimeOut(True);
  except
    on E: Exception do
      if csDesigning in ComponentState then
        ShowException(E, ExceptAddr) else
        raise;
  end;
  TIDACEvents.HandleObjectLoaded(Self);
end;

procedure TIfxConnection.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if Operation = opRemove then
    if AComponent = FSQLProcessor then
      FSQLProcessor := nil
    else if AComponent = FMonitor then
      FMonitor := nil
    else if AComponent = FFieldTypes then
      FFieldTypes := nil;
end;

procedure TIfxConnection.RegisterClient(Client: TObject; Event: TConnectChangeEvent = nil);
begin
  inherited;
  if Client is TIfxSQL then
    FStmts.Add(Client);
end;

procedure TIfxConnection.UnRegisterClient(Client: TObject);
begin
  inherited;
  if Client is TIfxSQL then
    FStmts.Remove(Client);
end;

procedure TIfxConnection.OpenDatabase(const Database: string; Exclusive: Boolean);
begin
  Activate;
  FResource.OpenDatabase(Database, Exclusive);
end;

procedure TIfxConnection.CloseDatabase;
begin
  if FResource <> nil then
  begin
    MakeCurrent;
    FResource.CloseDatabase;
  end;
end;

procedure TIfxConnection.ApplyUpdates(const DataSets: array of TDataSet);
var
  I: Integer;
begin
  if Length(DataSets) = 0 then Exit;

  for I := 0 to High(DataSets) do
    if (DataSets[I] as TIfxCustomDataSet).Connection <> Self then
      DatabaseError(Format(SUpdateWrongDatabase, [DataSets[I].Name, Name]));

  StartTransaction;
  try
    for I := 0 to High(DataSets) do
      TIfxCustomDataSet(DataSets[I]).ApplyUpdates(False);
    Commit;
  except
    Rollback;
    raise;
  end;
  for I := 0 to High(DataSets) do
    TIfxCustomDataSet(DataSets[I]).CommitUpdates;
end;

procedure TIfxConnection.CloseDataSets;
var
  I: Integer;
begin
  for I := 0 to DataSetCount - 1 do
    DataSets[I].Close;
end;

procedure TIfxConnection.StartTransaction;
begin
  Activate;
  FResource.BeginTransaction;
end;

procedure TIfxConnection.Commit;
begin
  MakeCurrent;
  FResource.Commit;
  CountHolder(0);
end;

procedure TIfxConnection.Rollback;
begin
  MakeCurrent;
  FResource.Rollback;
  CountHolder(0);
end;

procedure TIfxConnection.OpenTimeOutMode;
begin
  SetUseTimeOut(True);
end;

procedure TIfxConnection.CloseTimeOutMode;
begin
  SetUseTimeOut(False);
end;

function TIfxConnection.InterruptSQL: Boolean;
begin
  if sqldone <> SERVER_BUSY then
    IDACError(SConnectionIdle, Self);
  Result := sqlbreak = 0;
end;

procedure TIfxConnection.GetTableNames(Strings: TStrings);
begin
  GetTableNames(Strings, FTableTypes);
end;

procedure TIfxConnection.GetTableNames(Strings: TStrings; TableTypes: TIfxTableTypes);
var
  OwnerName: string;
begin
  Strings.Clear;

  with TIfxSQL.Create(nil) do
    try
      Connection := Self;
      SQL.Text := TABLE_NAMES_SQL;
      if ttUserTable in TableTypes then
        ParamByName('usertable').AsChar := 'T';
      if ttUserView in TableTypes then
        ParamByName('userview').AsChar := 'V';
      if ttUserPrivSyn in TableTypes then
        ParamByName('userprivsyn').AsChar := 'P';
      if ttUserPubSyn in TableTypes then
        ParamByName('userpubsyn').AsChar := 'S';
      if ttSysTable in TableTypes then
        ParamByName('systable').AsChar := 'T';
      if ttSysView in TableTypes then
        ParamByName('sysview').AsChar := 'V';
      if ttSysPrivSyn in TableTypes then
        ParamByName('sysprivsyn').AsChar := 'P';
      if ttSysPubSyn in TableTypes then
        ParamByName('syspubsyn').AsChar := 'S';
      Open;
      while not EOF do
      begin
        OwnerName := Trim(Fields[0].AsString);
        if OwnerName <> SessionUser then
          Strings.Add(Trim(Fields[0].AsString) + '.' + Trim(Fields[1].AsString)) else
          Strings.Add(Trim(Fields[1].AsString));
        Next;
      end;
    finally
      Free;
    end;
end;

procedure TIfxConnection.GetStoredProcNames(Strings: TStrings);
begin
  Strings.Clear;

  with TIfxSQL.Create(nil) do
    try
      Connection := Self;
      SQL.Text := PROC_NAMES_SQL;
      Open;
      while not EOF do
      begin
        Strings.Add(Fields[0].AsString);
        Next;
      end;
    finally
      Free;
    end;
end;

function TIfxConnection.AllocateStatement(const SQL: string;
  Cacheable: Boolean): TIfxStatementResource;
begin
  Activate;
  Result := FResource.AllocateStatement(SQL, Cacheable);
end;

function TIfxConnection.Execute(const SQL: string; Params: TObject = nil): int4;
begin
  Activate;
  Result := FResource.Execute(SQL, Params);
end;

function TIfxConnection.LastInsertedSerial: int4;
begin
  MakeCurrent;
  Result := SQLCA^.sqlerrd[1];
end;

function TIfxConnection.LastInsertedRowID: int4;
begin
  MakeCurrent;
  Result := SQLCA^.sqlerrd[5];
end;

function TIfxConnection.LastInsertedSerial8: TIfx_int8;
begin
  MakeCurrent;
  ifx_getserial8(@Result);
end;

procedure TIfxConnection.HandleDatabaseChange;
var
  I: Integer;
begin
  for I := 0 to FClients.Count - 1 do
    TIfxConnectionClient(FClients[I]).HandleDatabaseChange;
  SendConnectEvent(False);
end;

function TIfxConnection.GetVersion: string;
begin
  Result := IDACVersion;
end;

procedure TIfxConnection.SetVersion(const Value: string);
begin
end;

procedure TIfxConnection.SetIsolationLevel(Value: TIfxIsolationLevel);
begin
  if FResource <> nil then
  begin
    FResource.MakeCurrent;
    FResource.SetIsolation(Value);
  end;
  FParams.IsolationLevel := Value;
end;

function TIfxConnection.GetAnsiAutoCommit: Boolean;
begin
  Result := FParams.AnsiAutoCommit;
end;

function TIfxConnection.GetCommitRetain: Boolean;
begin
  Result := FParams.CommitRetain;
end;

function TIfxConnection.GetHandle: string;
begin
  CheckActive;
  Result := FResource.Handle;
end;

function TIfxConnection.GetInTransaction: Boolean;
begin
  Result := FResource.InTransaction;
end;

function TIfxConnection.GetIsAnsiDatabase: Boolean;
begin
  Result := FResource.IsAnsiDatabase;
end;

function TIfxConnection.GetIsolationLevel: TIfxIsolationLevel;
begin
  Result := FParams.IsolationLevel;
end;

function TIfxConnection.GetIsTransactionsSupported: Boolean;
begin
  Result := FResource.IsTransactionsSupported;
end;

function TIfxConnection.GetKeepConnection: Boolean;
begin
  Result := FParams.KeepConnection;
end;

function TIfxConnection.GetParams: TStrings;
begin
  Result := FParams.Environment;
end;

function TIfxConnection.GetSafeMode: TIfxSafeMode;
begin
  Result := FParams.SafeMode;
end;

function TIfxConnection.GetTransactionActionOnClose: TIfxTransactionAction;
begin
  Result := FParams.TransactionActionOnClose;
end;

procedure TIfxConnection.SetAnsiAutoCommit(Value: Boolean);
begin
  FParams.AnsiAutoCommit := Value;
end;

procedure TIfxConnection.SetCommitRetain(Value: Boolean);
begin
  FParams.CommitRetain := Value;
end;

procedure TIfxConnection.SetSafeMode(Value: TIfxSafeMode);
begin
  FParams.SafeMode := Value;
end;

procedure TIfxConnection.SetTransactionActionOnClose(Value: TIfxTransactionAction);
begin
  FParams.TransactionActionOnClose := Value;
end;

function TIfxConnection.GetPoolOptions: TLXResourcePoolOptions;
begin
  Result := FParams.PoolOptions;
end;

procedure TIfxConnection.SetPoolOptions(Value: TLXResourcePoolOptions);
begin
  FParams.PoolOptions.Assign(Value);
end;

function TIfxConnection.GetStatementCacheSize: Integer;
begin
  Result := FParams.StatementCacheSize;
end;

procedure TIfxConnection.SetStatementCacheSize(Value: Integer);
begin
  FParams.StatementCacheSize := Value;
end;

function TIfxConnection.GetStatementAllocations: Cardinal;
begin
  CheckActive;
  Result := FResource.StatementAllocations;
end;

function TIfxConnection.GetStatementCacheHits: Cardinal;
begin
  CheckActive;
  Result := FResource.StatementCacheHits;
end;

function TIfxConnection.GetShareCursorStatement: Boolean;
begin
  Result := FParams.ShareCursorStatement;
end;

procedure TIfxConnection.SetShareCursorStatement(Value: Boolean);
begin
  FParams.ShareCursorStatement := Value;
end;

procedure TIfxConnection.SetSQLProcessor(Value: TIfxSQLProcessor);
begin
  if Value <> FSQLProcessor then
  begin
    if FSQLProcessor <> nil then
      FSQLProcessor.RemoveFreeNotification(Self);
    FSQLProcessor := Value;
    if FSQLProcessor <> nil then
      FSQLProcessor.FreeNotification(Self);
  end;
end;

procedure TIfxConnection.SetMonitor(Value: TIfxCustomMonitor);
begin
  if Value <> FMonitor then
  begin
    if FMonitor <> nil then
      FMonitor.RemoveFreeNotification(Self);
    FMonitor := Value;
    if FMonitor <> nil then
      FMonitor.FreeNotification(Self);
  end;
end;

procedure TIfxConnection.SetFieldTypes(Value: TIfxFieldTypes);
begin
  if Value <> FFieldTypes then
  begin
    if FFieldTypes <> nil then
      FFieldTypes.RemoveFreeNotification(Self);
    FFieldTypes := Value;
    if FFieldTypes <> nil then
      FFieldTypes.FreeNotification(Self);
  end;
end;

function TIfxConnection.GetActiveSQLProcessor: TIfxSQLProcessor;
begin
  Result := FSQLProcessor;
  if Result = nil then
    Result := TIfxSQLProcessor.DefaultProcessor;
end;

function TIfxConnection.GetActiveFieldTypes: TIfxFieldTypes;
begin
  Result := FFieldTypes;
  if Result = nil then
    Result := TIfxFieldTypes.DefaultTypes;
end;

function TIfxConnection.GetRowsAffected: int4;
begin
  Result := SQLCA^.sqlerrd[2];
end;


{ TIfxConnectionClient }

destructor TIfxConnectionClient.Destroy;
begin
  if Assigned(FConnection) then
    UnregisterConnection;
  inherited;
end;

function TIfxConnectionClient.CheckConnection: TIfxConnection;
begin
  Result := IfxConnection.CheckConnection(FConnection, Self);
end;

procedure TIfxConnectionClient.CountHolder(Value: Integer);
begin
  if FConnection <> nil then
    FConnection.CountHolder(Value);
end;

function TIfxConnectionClient.GetConnection: TIfxConnection;
begin
  Result := FConnection;
end;

procedure TIfxConnectionClient.SetConnection(Value: TIfxConnection);
begin
  if FConnection = Value then Exit;

  if Assigned(FConnection) then
  begin
    ConnectionChanging;
    ConnectionClosing;
    UnregisterConnection;
  end;

  FConnection := Value;

  if Assigned(FConnection) then
    RegisterConnection;
end;

procedure TIfxConnectionClient.RegisterConnection;
begin
  FConnection.FClients.Add(Self);
end;

procedure TIfxConnectionClient.UnregisterConnection;
begin
  FConnection.FClients.Remove(Self);
end;

procedure TIfxConnectionClient.RegisterConnectionClient(Client: TObject;
  Event: TConnectChangeEvent);
begin
  FConnection.RegisterClient(Client, Event);
end;

procedure TIfxConnectionClient.UnregisterConnectionClient(Client: TObject);
begin
  FConnection.UnRegisterClient(Client);
end;

function TIfxConnectionClient.AllocateStatement(const SQL: string;
  Cacheable: Boolean): TIfxStatementResource;
begin
  Result := FConnection.AllocateStatement(SQL, Cacheable);
end;

procedure TIfxConnectionClient.NotifyMonitor(EventType: TIfxMonitorEventType;
  const Format: string; const Args: array of const);
begin
  FConnection.NotifyMonitor(EventType, Format, Args);
end;

procedure TIfxConnectionClient.NotifyMonitor(EventType: TIfxMonitorEventType;
  MessageProc: TIfxMonitorMessageProc; const Args: array of const);
begin
  FConnection.NotifyMonitor(EventType, MessageProc, Args);
end;


initialization
  globalConnectionPools := TLXResourcePools.Create;

finalization
  globalConnectionPools.Free;

end.